// synch.cc 
//    Routines for synchronizing threads.  Three kinds of
//    synchronization routines are defined here: semaphores, locks
//       and condition variables (the implementation of the last two
//    are left to the reader).
//
// Any implementation of a synchronization routine needs some
// primitive atomic operation.  We assume Nachos is running on
// a uniprocessor, and thus atomicity can be provided by
// turning off interrupts.  While interrupts are disabled, no
// context switch can occur, and thus the current thread is guaranteed
// to hold the CPU throughout, until interrupts are reenabled.
//
// Because some of these routines might be called with interrupts
// already disabled (Semaphore::V for one), instead of turning
// on interrupts at the end of the atomic operation, we always simply
// re-set the interrupt state back to its original value (whether
// that be disabled or enabled).
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "synch.h"
#include "system.h"
//This is a comment test by jorge
//Another comment by jorge on the google code repo!

//----------------------------------------------------------------------
// Semaphore::Semaphore
//     Initialize a semaphore, so that it can be used for synchronization.
//
//    "debugName" is an arbitrary name, useful for debugging.
//    "initialValue" is the initial value of the semaphore.
//----------------------------------------------------------------------

Semaphore::Semaphore(char* debugName, int initialValue)
{
    name = debugName;
    value = initialValue;
    queue = new List;
}

//----------------------------------------------------------------------
// Semaphore::Semaphore
//     De-allocate semaphore, when no longer needed.  Assume no one
//    is still waiting on the semaphore!
//----------------------------------------------------------------------

Semaphore::~Semaphore()
{
    delete queue;
}

//----------------------------------------------------------------------
// Semaphore::P
//     Wait until semaphore value > 0, then decrement.  Checking the
//    value and decrementing must be done atomically, so we
//    need to disable interrupts before checking the value.
//
//    Note that Thread::Sleep assumes that interrupts are disabled
//    when it is called.
//----------------------------------------------------------------------

void
Semaphore::P()
{
    IntStatus oldLevel = interrupt->SetLevel(IntOff);    // disable interrupts
    
    while (value == 0) {             // semaphore not available
    queue->Append((void *)currentThread);    // so go to sleep
    currentThread->Sleep();
    } 
    value--;                     // semaphore available,
                        // consume its value
    
    (void) interrupt->SetLevel(oldLevel);    // re-enable interrupts
}

//----------------------------------------------------------------------
// Semaphore::V
//     Increment semaphore value, waking up a waiter if necessary.
//    As with P(), this operation must be atomic, so we need to disable
//    interrupts.  Scheduler::ReadyToRun() assumes that threads
//    are disabled when it is called.
//----------------------------------------------------------------------

void
Semaphore::V()
{
    Thread *thread;
    IntStatus oldLevel = interrupt->SetLevel(IntOff);

    thread = (Thread *)queue->Remove();
    if (thread != NULL)       // make thread ready, consuming the V immediately
    scheduler->ReadyToRun(thread);
    value++;
    (void) interrupt->SetLevel(oldLevel);
}

// Dummy functions -- so we can compile our later assignments 
// Note -- without a correct implementation of Condition::Wait(), 
// the test case in the network assignment won't work!

//This is a comment added by Jorge
Lock::Lock(char* debugName) {
#ifdef CHANGED
    name = debugName;
    waitQueue = new List;
    currentState = FREE;
#endif
}
Lock::~Lock() {
#ifdef CHANGED
    delete waitQueue;
#endif
}
void Lock::Acquire() {
#ifdef CHANGED
    //disable interupts
    IntStatus old = interrupt->SetLevel(IntOff);

    if(currentThread == currentOwner) { //am I the lock owner?
        interrupt->SetLevel(old); //Since I already own the lock...
        return;                   //done and done
    }

    // I'm not the lock owner t.t
    if(currentState == FREE) {
        currentState = BUSY;
        currentOwner = currentThread;
    } else { //lock is BUSY, but we want the lock
        waitQueue->Append(currentThread);
        currentThread->Sleep();
    }

    //enable interupts
    interrupt->SetLevel(old);
#endif
}
void Lock::Release() {
#ifdef CHANGED
    // disable interrupts
    IntStatus old = interrupt->SetLevel(IntOff);

    //check if we own the lock
    if(currentThread != currentOwner) {
        //we can't release a lock we don't own, so alert and back out.
		#ifdef USER_PROGRAM
        printf("[KERNEL PID#%i, TID#%i] %s tried to Release (%s), which they do not own.\n",currentThread->getProcessID(), currentThread->getThreadID(), currentThread->getName(), name);
		#endif
        interrupt->SetLevel(old);
        return;
    }

    //Ok, so I am the owner, lets release this lock into someone's care, or set it free
    if(!waitQueue->IsEmpty()) { // there was someone waiting
        Thread* nextInLine = (Thread *)waitQueue->Remove();
        //note: since ReadyToRun makes the state of the thread Ready, we don't have to here
        scheduler->ReadyToRun(nextInLine); //put that thread in the Ready State
        currentOwner = nextInLine;
    } else { // no one is waiting for the thread, time to set it free
        currentState = FREE;
        currentOwner = NULL;
    }

    //restore interrupts
    interrupt->SetLevel(old);
#endif
}

// Code Below have been added by Parvaneh Esk.
bool Lock::isHeldByCurrentThread() {
#ifdef CHANGED
	return currentThread == currentOwner;
						// true if the current thread
						// holds this lock.  Useful for
						// checking in Release, and in
						// Condition variable ops below.
#endif
}

Condition::Condition(char* debugName) {
#ifdef CHANGED
  name = debugName;
  waitQueue = new List();
  waitingLock = NULL;
#endif
}

Condition::~Condition() {
#ifdef CHANGED
  delete waitQueue;
  waitingLock = NULL;
#endif
}

void Condition::Wait(Lock* conditionLock) {
#ifdef CHANGED
    //disable interupts
    IntStatus old = interrupt->SetLevel(IntOff);

    if(conditionLock == NULL) {
        //restore interrupts
        interrupt->SetLevel(old);
        return;
    }

    if(waitingLock == NULL) {
        // this is the first person waiting on this lock
        // save the lock internally
        waitingLock = conditionLock;
    }

    if(waitingLock != conditionLock) {
		#ifdef USER_PROGRAM
        printf("[KERNEL PID#%i, TID#%i] You passed in a lock (%s) that this condition variable is not set to wait on (%s).\n",currentThread->getProcessID(), currentThread->getThreadID(), conditionLock->getName(), waitingLock->getName());
		#endif
        //restore interrupts
        interrupt->SetLevel(old);
        return;
    }

    // if we got here we can wait on this lock, so release, and sleep, and then
    // reaquire when signalles
    waitQueue->Append(currentThread);
    conditionLock->Release();
    currentThread->Sleep();

    // ok we woke up, woot, lets get the lock
    conditionLock->Acquire();

    //restore interrupts
    interrupt->SetLevel(old);
#endif
}

void Condition::Signal(Lock* conditionLock) {
#ifdef CHANGED
    //disable interupts
    IntStatus old = interrupt->SetLevel(IntOff);
    if(waitQueue->IsEmpty()) {
		#ifdef USER_PROGRAM
        printf("[KERNEL PID#%i, TID#%i] Tried to signal on a lock (%s) with no one waiting.\n",currentThread->getProcessID(), currentThread->getThreadID(), conditionLock->getName());
		#endif
        //restore interrupts
        interrupt->SetLevel(old);
        return;
    }

    if(waitingLock != conditionLock) {
		#ifdef USER_PROGRAM
        printf("[KERNEL PID#%i, TID#%i] you passed in a lock (%s) that this condition variable is not set to signal on.\n",currentThread->getProcessID(), currentThread->getThreadID(), conditionLock->getName());
		#endif
        //restore interrupts
        interrupt->SetLevel(old);
        return;
    }

    Thread* nextInLine = (Thread *)waitQueue->Remove();
    //note: since ReadyToRun makes the state of the thread Ready, we don't have to here
    scheduler->ReadyToRun(nextInLine); //put that thread in the Ready State

    if(waitQueue->IsEmpty()) {
        // wiat queue is now empty, so lets clear the current waiting lock
        waitingLock = NULL;
    }
    //restore interrupts
    interrupt->SetLevel(old);
#endif
}
void Condition::Broadcast(Lock* conditionLock) {
#ifdef CHANGED
    while(!waitQueue->IsEmpty()) {
        Signal(conditionLock);
    }
#endif
}
